#include "U2Viewport.h"

#include "U2LogManager.h"
#include "U2RenderTarget.h"
#include "U2Camera.h"
#include "U2RenderSystem.h"
#include "U2Root.h"



U2EG_NAMESPACE_USING


OrientationMode     U2Viewport::mDefaultOrientationMode     = OR_DEGREE_0;


//---------------------------------------------------------------------
U2Viewport::U2Viewport(U2Camera* cam, U2RenderTarget* target, u2real left, u2real top, u2real width, u2real height, int ZOrder)
: mCamera(cam)
, mTarget(target)
, mRelLeft(left)
, mRelTop(top)
, mRelWidth(width)
, mRelHeight(height)
// Actual dimensions will update later
, mZOrder(ZOrder)
, mBackColour(U2ColourValue::Black)
, mDepthClearValue(1)
, mClearEveryFrame(true)
, mClearBuffers(FBT_COLOUR | FBT_DEPTH)
, mUpdated(false)
, mIsAutoUpdated(true)
, mVisibilityMask(0xFFFFFFFF)
{			
#if U2_COMPILER != U2_COMPILER_GCCE && U2_PLATFORM != U2_PLATFORM_ANDROID
    U2LogManager::getSingleton().stream(LML_TRIVIAL)
        << "Creating viewport on target '" << target->getName() << "'"
        << ", rendering from camera '" << (cam != 0 ? cam->getName() : "NULL") << "'"
        << ", relative dimensions "	<< std::ios::fixed << std::setprecision(2) 
        << "L: " << left << " T: " << top << " W: " << width << " H: " << height
        << " ZOrder: " << ZOrder;
#endif

    
    // Set the default orientation mode
    mOrientationMode = mDefaultOrientationMode;


    // Calculate actual dimensions
    _updateDimensions();

    // notify camera
    if(cam) cam->_notifyViewport(this);
}
//---------------------------------------------------------------------
U2Viewport::~U2Viewport()
{
    U2RenderSystem* rs = U2Root::getSingleton().getRenderSystem();
    if ((rs) && (rs->_getViewport() == this))
    {
        rs->_setViewport(NULL);
    }
}

//---------------------------------------------------------------------
int U2Viewport::getZOrder(void) const
{
    return mZOrder;
}
//---------------------------------------------------------------------
U2RenderTarget* U2Viewport::getTarget(void) const
{
    return mTarget;
}
//---------------------------------------------------------------------
U2Camera* U2Viewport::getCamera(void) const
{
    return mCamera;
}
//---------------------------------------------------------------------
u2real U2Viewport::getLeft(void) const
{
    return mRelLeft;
}
//---------------------------------------------------------------------
u2real U2Viewport::getTop(void) const
{
    return mRelTop;
}
//---------------------------------------------------------------------
u2real U2Viewport::getWidth(void) const
{
    return mRelWidth;
}
//---------------------------------------------------------------------
u2real U2Viewport::getHeight(void) const
{
    return mRelHeight;
}
//---------------------------------------------------------------------
int U2Viewport::getActualLeft(void) const
{
    return mActLeft;
}
//---------------------------------------------------------------------
int U2Viewport::getActualTop(void) const
{
    return mActTop;
}
//---------------------------------------------------------------------
int U2Viewport::getActualWidth(void) const
{
    return mActWidth;
}
//---------------------------------------------------------------------
int U2Viewport::getActualHeight(void) const
{
    return mActHeight;
}
//---------------------------------------------------------------------
void U2Viewport::setDimensions(u2real left, u2real top, u2real width, u2real height)
{
    mRelLeft = left;
    mRelTop = top;
    mRelWidth = width;
    mRelHeight = height;
    _updateDimensions();
}
//---------------------------------------------------------------------
void U2Viewport::setBackgroundColour(const U2ColourValue& colour)
{
    mBackColour = colour;
}
//---------------------------------------------------------------------
const U2ColourValue& U2Viewport::getBackgroundColour(void) const
{
    return mBackColour;
}
//---------------------------------------------------------------------
void U2Viewport::setDepthClear( u2real depth )
{
    mDepthClearValue = depth;
}
//---------------------------------------------------------------------
u2real U2Viewport::getDepthClear(void) const
{
    return mDepthClearValue;
}
//---------------------------------------------------------------------
void U2Viewport::setAutoUpdated(bool inAutoUpdated)
{
    mIsAutoUpdated = inAutoUpdated;
}
//---------------------------------------------------------------------
bool U2Viewport::isAutoUpdated() const
{
    return mIsAutoUpdated;
}
//---------------------------------------------------------------------
void U2Viewport::getActualDimensions(int &left, int&top, int &width, int &height) const
{
    left = mActLeft;
    top = mActTop;
    width = mActWidth;
    height = mActHeight;
}
//---------------------------------------------------------------------
bool U2Viewport::_isUpdated(void) const
{
    return mUpdated;
}
//---------------------------------------------------------------------
void U2Viewport::_clearUpdatedFlag(void)
{
    mUpdated = false;
}
//-----------------------------------------------------------------------
void U2Viewport::pointOrientedToScreen(const U2Vector2 &v, int orientationMode, U2Vector2 &outv)
{
    pointOrientedToScreen(v.x, v.y, orientationMode, outv.x, outv.y);
}
//-----------------------------------------------------------------------
void U2Viewport::pointOrientedToScreen(u2real orientedX, u2real orientedY, int orientationMode,
                                     u2real &screenX, u2real &screenY)
{
    u2real orX = orientedX;
    u2real orY = orientedY;
    switch (orientationMode)
    {
    case 1:
        screenX = orY;
        screenY = u2real(1.0) - orX;
        break;
    case 2:
        screenX = u2real(1.0) - orX;
        screenY = u2real(1.0) - orY;
        break;
    case 3:
        screenX = u2real(1.0) - orY;
        screenY = orX;
        break;
    default:
        screenX = orX;
        screenY = orY;
        break;
    }
}
//-----------------------------------------------------------------------
void U2Viewport::addListener(Listener* l)
{
    if (std::find(mListeners.begin(), mListeners.end(), l) == mListeners.end())
        mListeners.push_back(l);
}
//-----------------------------------------------------------------------
void U2Viewport::removeListener(Listener* l)
{
    ListenerList::iterator i = std::find(mListeners.begin(), mListeners.end(), l);
    if (i != mListeners.end())
        mListeners.erase(i);
}

//---------------------------------------------------------------------
void U2Viewport::setOrientationMode(OrientationMode orientationMode, bool setDefault)
{
#if U2_NO_VIEWPORT_ORIENTATIONMODE != 0
    U2_EXCEPT(U2Exception::ERR_NOT_IMPLEMENTED,
        "Setting U2Viewport orientation mode is not supported",
        __FUNCTION__);
#endif
    mOrientationMode = orientationMode;

    if (setDefault)
    {
        setDefaultOrientationMode(orientationMode);
    }

    if (mCamera)
    {
        mCamera->setOrientationMode(mOrientationMode);
    }

    // Update the render system config
#if U2_PLATFORM == U2_PLATFORM_APPLE_IOS
    U2RenderSystem* rs = U2Root::getSingleton().getRenderSystem();
    if(mOrientationMode == OR_LANDSCAPELEFT)
        rs->setConfigOption("Orientation", "Landscape Left");
    else if(mOrientationMode == OR_LANDSCAPERIGHT)
        rs->setConfigOption("Orientation", "Landscape Right");
    else if(mOrientationMode == OR_PORTRAIT)
        rs->setConfigOption("Orientation", "Portrait");
#endif
}
//---------------------------------------------------------------------
OrientationMode U2Viewport::getOrientationMode() const
{
#if U2_NO_VIEWPORT_ORIENTATIONMODE != 0
    U2_EXCEPT(U2Exception::ERR_NOT_IMPLEMENTED,
        "Getting U2Viewport orientation mode is not supported",
        __FUNCTION__);
#endif
    return mOrientationMode;
}
//---------------------------------------------------------------------
void U2Viewport::setDefaultOrientationMode(OrientationMode orientationMode)
{
#if U2_NO_VIEWPORT_ORIENTATIONMODE != 0
    U2_EXCEPT(U2Exception::ERR_NOT_IMPLEMENTED,
        "Setting default U2Viewport orientation mode is not supported",
        __FUNCTION__);
#endif
    mDefaultOrientationMode = orientationMode;
}
//---------------------------------------------------------------------
OrientationMode U2Viewport::getDefaultOrientationMode()
{
#if U2_NO_VIEWPORT_ORIENTATIONMODE != 0
    U2_EXCEPT(U2Exception::ERR_NOT_IMPLEMENTED,
        "Getting default U2Viewport orientation mode is not supported",
        __FUNCTION__);
#endif
    return mDefaultOrientationMode;
}

//---------------------------------------------------------------------
unsigned int U2Viewport::_getNumRenderedFaces(void) const
{
    return mCamera ? mCamera->_getNumRenderedFaces() : 0;
}
//---------------------------------------------------------------------
unsigned int U2Viewport::_getNumRenderedBatches(void) const
{
    return mCamera ? mCamera->_getNumRenderedBatches() : 0;
}

//---------------------------------------------------------------------
void U2Viewport::setCamera(U2Camera* cam)
{
    if(mCamera)
    {
        if(mCamera->getViewport() == this)
        {
            mCamera->_notifyViewport(0);
        }
    }

    mCamera = cam;
    if (cam)
    {
        // update aspect ratio of new camera if needed.
        if (cam->getAutoAspectRatio())
        {
            cam->setAspectRatio((u2real) mActWidth / (u2real) mActHeight);
        }
#if U2_NO_VIEWPORT_ORIENTATIONMODE == 0
        cam->setOrientationMode(mOrientationMode);
#endif
        cam->_notifyViewport(this);
    }

    for (ListenerList::iterator i = mListeners.begin(); i != mListeners.end(); ++i)
    {
        (*i)->viewportCameraChanged(this);
    }
}

//---------------------------------------------------------------------
void U2Viewport::_updateDimensions(void)
{
    u2real height = (u2real) mTarget->getHeight();
    u2real width = (u2real) mTarget->getWidth();

    mActLeft = (int) (mRelLeft * width);
    mActTop = (int) (mRelTop * height);
    mActWidth = (int) (mRelWidth * width);
    mActHeight = (int) (mRelHeight * height);

    // This will check if the cameras getAutoAspectRatio() property is set.
    // If it's true its aspect ratio is fit to the current viewport
    // If it's false the camera remains unchanged.
    // This allows cameras to be used to render to many viewports,
    // which can have their own dimensions and aspect ratios.

    if (mCamera) 
    {
        if (mCamera->getAutoAspectRatio())
            mCamera->setAspectRatio((u2real) mActWidth / (u2real) mActHeight);

#if U2_NO_VIEWPORT_ORIENTATIONMODE == 0
        mCamera->setOrientationMode(mOrientationMode);
#endif
    }

#if U2_COMPILER != U2_COMPILER_GCCE
    U2LogManager::getSingleton().stream(LML_TRIVIAL)
        << "U2Viewport for camera '" << (mCamera != 0 ? mCamera->getName() : "NULL") << "'"
        << ", actual dimensions "	<< std::ios::fixed << std::setprecision(2) 
        << "L: " << mActLeft << " T: " << mActTop << " W: " << mActWidth << " H: " << mActHeight;
#endif

    mUpdated = true;

    for (ListenerList::iterator i = mListeners.begin(); i != mListeners.end(); ++i)
    {
        (*i)->viewportDimensionsChanged(this);
    }
}

//---------------------------------------------------------------------
void U2Viewport::update(void)
{
    if (mCamera)
    {
        // Tell Camera to render into me
        mCamera->_renderScene(this);
    }
}

//---------------------------------------------------------------------
bool U2Viewport::getClearEveryFrame(void) const
{
    return mClearEveryFrame;
}
//---------------------------------------------------------------------
unsigned int U2Viewport::getClearBuffers(void) const
{
    return mClearBuffers;
}